package com.whf.android.jar.util.number;

import androidx.annotation.NonNull;

import com.blankj.utilcode.util.NumberUtils;
import com.blankj.utilcode.util.ObjectUtils;

import java.math.BigDecimal;
import java.text.DecimalFormat;


/**
 * 由于Java的简单类型不能够精确的对浮点数进行运算，这个工具类提供精
 * 确的浮点数运算，包括加减乘除和四舍五入。
 *
 * @author qf
 * @author wang.hai.feng
 * @version 2.5
 */
public final class DoubleUtils {

    /**
     * 默认除法运算精度
     * <p>
     * 由于Java的简单类型不能够精确的对浮点数进行运算，这个工具类提供精
     * 确的浮点数运算，包括加减乘除和四舍五入。
     */
    private static final int DEF_DIV_SCALE = 10;

    /**
     * 提供精确的加法运算。
     *
     * @param v1 被加数
     * @param v2 加数
     * @return 两个参数的和
     */
    public static double add(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.add(b2).doubleValue();
    }

    /**
     * 提供精确的减法运算。
     *
     * @param v1 被减数
     * @param v2 减数
     * @return 两个参数的差
     */
    public static double sub(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.subtract(b2).doubleValue();
    }

    /**
     * 提供精确的乘法运算。
     *
     * @param v1 被乘数
     * @param v2 乘数
     * @return 两个参数的积
     */
    public static double mul(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.multiply(b2).doubleValue();
    }

    /**
     * 提供（相对）精确的除法运算，当发生除不尽的情况时，精确到
     * 小数点以后10位，以后的数字四舍五入。
     *
     * @param v1 被除数
     * @param v2 除数
     * @return 两个参数的商
     */
    public static double div(double v1, double v2) {
        return div(v1, v2, DEF_DIV_SCALE);
    }

    /**
     * 提供（相对）精确的除法运算。当发生除不尽的情况时，由scale参数指
     * 定精度，以后的数字四舍五入。
     *
     * @param v1    被除数
     * @param v2    除数
     * @param scale 表示表示需要精确到小数点以后几位。
     * @return 两个参数的商
     */
    public static double div(double v1, double v2, int scale) {
        if (v2 == 0) {
            //除数不能为0；
            return 0;
        }
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
     * 提供精确的加法运算。
     *
     * @return 多个个参数的和
     */
    public static double adds(Object... objects) {
        double bi = 0;
        if (objects == null) {
            return bi;
        }
        for (Object object : objects) {
            bi = add(bi, objToDouble(object));
        }
        return bi;
    }

    /**
     * 提供精确的小数位四舍五入处理。
     *
     * @param v     需要四舍五入的数字
     * @param scale 小数点后保留几位
     * @return 四舍五入后的结果
     */
    public static double round(double v, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b = new BigDecimal(Double.toString(v));
        BigDecimal one = new BigDecimal("1");
        return b.divide(one, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
     * @param obj：吨转换为万吨
     */
    public static double toUnitWan(double obj) {
        return toUnit(obj, 4);
    }

    /**
     * @param obj：吨转换为万吨
     */
    public static double toUnitWan(Object obj) {
        return toUnit(obj, 4);
    }

    /***
     * 如 进2位除以100，进3位除以1k，进4位除以1w；
     * @param obj：数据进位
     */
    public static double toUnit(Object obj, int carry) {
        double data = objToDouble(obj);
        if (carry < 1) {
            return data;
        }
        int bit = 1;
        for (int i = 0; i < carry; i++) {
            bit = bit * 10;
        }
        return div(data, bit, 2);
    }

    /**
     * @param obj：角度转换为百分数
     */
    public static double angleToRate(double obj) {
        return angleToRate(obj, 2);
    }

    /**
     * @param obj：角度转换为百分数
     */
    public static double angleToRate(double obj, int fractionDigits) {
        return div(mul(obj, 10), 36, fractionDigits);
    }

    /**
     * 保留小数点后2位
     *
     * @param d:
     */
    public static String format(double d) {
        return NumberUtils.format(d, 2);
    }

    /**
     * 保留小数点后几位
     *
     * @param d:
     * @param fractionDigits:保留小数点后de位数
     */
    public static String format(double d, int fractionDigits) {
        return NumberUtils.format(d, fractionDigits);
    }

    /***
     * 千位分隔符
     * @param d:
     */
    @NonNull
    public static String dff(String d) {
        return dff(Double.parseDouble(d));
    }

    /***
     * 千位分隔符
     * @param d:
     */
    @NonNull
    public static String dff(double d) {
        DecimalFormat df = new DecimalFormat("#,###,###,###.###");
        return df.format(d);
    }

    /**
     * @param obj：是否是数字
     */
    public static boolean isDouble(Object obj) {
        try {
            Double.parseDouble(String.valueOf(obj));
            return false;
        } catch (Exception e) {
            return true;
        }
    }

    /**
     * @param obj：Object转换为double
     */
    public static double objToDouble(Object obj) {
        if (ObjectUtils.isEmpty(obj) || "null".equals(obj) || isDouble(obj)) {
            obj = "0";
        }
        return Double.parseDouble(String.valueOf(obj));
    }

    /**
     * @param obj：Object转换为int
     */
    public static int objToInt(Object obj) {
        return (int) objToDouble(obj);
    }

    /**
     * @param obj：Object转换为long
     */
    public static long objToLong(Object obj) {
        return (long) objToDouble(obj);
    }

    /**
     * @param obj：Object转换为int再转String
     */
    @NonNull
    public static String objToIntStr(Object obj) {
        return String.valueOf(objToInt(obj));
    }

    /**
     * @param obj：Object转换为float
     */
    public static float objToFloat(Object obj) {
        if (ObjectUtils.isEmpty(obj) || "null".equals(obj) || isDouble(obj)) {
            obj = "0";
        }
        return Float.parseFloat(String.valueOf(obj));
    }

}
